/*
 * Created on 02/apr/07
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package it.unibo.deis.lia.mmhc.mpe;

import java.util.*;

import it.unibo.deis.lia.filters.*;
import it.unibo.deis.lia.location.*;
import it.unibo.deis.lia.mmhc.nip.NetworkInterfaceProvider;
import it.unibo.deis.lia.networkInterface.*;
import it.unibo.deis.lia.filters.graphic.FunctionFrame;

public class MobilityEstimatorIEEE80211 extends Thread {

	private boolean debug = false;

	private IEEE80211 ieee80211;
	private int samplePeriod = 250;
	private int harmonic = 1;

	private float mobilityDegree = -1; // CMob
	private int mobilityState = MobilityEstimator.NOT_AVAILABLE;
	private Vector<LocationOss> jointVector = new Vector<LocationOss>();

	private boolean ok = true;
	private float upperBound = (float) 0.85;
	private float lowerBound = (float) 0.35;// 0.40;

	/*
	 * protected MobilityEstimatorIEEE80211()throws Exception{ this(0); }
	 */

	protected MobilityEstimatorIEEE80211(/* int interfaceNumber */)
			throws Exception {
		// networkInterface80211=new IEEE80211(interfaceNumber);
		NetworkInterfaceProvider networkInterfaceProvider = NetworkInterfaceProvider
				.getInstance();
		// TODO (opzionale) bisogna gestire la possibilita' che ci siano piu'
		// interfacce IEEE80211
		Vector<GenericNetworkInterface> availableInterfaces = networkInterfaceProvider
				.getAvailableInterfaces();
		for (int i = 0; i < availableInterfaces.size(); i++) {
			if (availableInterfaces.elementAt(i) instanceof IEEE80211) {
				ieee80211 = (IEEE80211) availableInterfaces.elementAt(i);
				break;
			}
		}
		if (ieee80211 == null)
			throw new Exception("MobilityEstimatorIEEE80211 unavailable");
	}

	protected float getMobilityDegree() {
		return mobilityDegree;
	}

	protected int getMobilityState() {
		return mobilityState;
	}

	protected void stopMobilityEstimatorIEEE80211() {
		ok = false;
	}

	/*
	 * getJoint() immediately provides monitored peer set and related Joint
	 * estimations
	 */
	protected Vector<LocationOss> getJoint() {
		return jointVector;
	}

	public void run() {

		// boolean isNoiseEnvironment=true;//isNoise(20);
		// System.out.println("isNoiseEnvironment: "+isNoiseEnvironment);
		// System.exit(0);

		FilterManager filterManagerAdhoc = null;// =new FilterManager();
		// if(isNoiseEnvironment){
		filterManagerAdhoc = new FilterManager(new Fourier());
		filterManagerAdhoc.setFilterMemory(16);
		// }
		// else{
		// filterManager=new FilterManager(new Identity());
		// }
		filterManagerAdhoc.setMinimizeValueSet(true);

		FilterManager filterManagerInfrastructure = null;// =new
															// FilterManager();
		// if(isNoiseEnvironment){
		filterManagerInfrastructure = new FilterManager(new Fourier());
		filterManagerInfrastructure.setFilterMemory(16);
		// }
		// else{
		// filterManager=new FilterManager(new Identity());
		// }
		filterManagerInfrastructure.setMinimizeValueSet(true);

		// frame to visualize RSSI trace (optional)
		FunctionFrame frameRSSIAdhoc = null;
		FunctionFrame filteredRSSIAdhoc = null;
		if (debug) {
			int frameWidth = 900, frameHeight = 240;
			frameRSSIAdhoc = new FunctionFrame("test (actual)",
					filterManagerAdhoc.getActualDataDB(), 95, 40);
			frameRSSIAdhoc.setBounds(10, 10, frameWidth, frameHeight);
			frameRSSIAdhoc.setVisible(true);
			frameRSSIAdhoc.toFront();

			filteredRSSIAdhoc = new FunctionFrame("test (filtered)",
					filterManagerAdhoc.getFilteredDataDB(), 95, 40);
			filteredRSSIAdhoc.setBounds(10, 10 + frameHeight, frameWidth,
					frameHeight);
			filteredRSSIAdhoc.setVisible(true);
			filteredRSSIAdhoc.toFront();
		}

		FunctionFrame frameRSSIInfratructure = null;
		FunctionFrame filteredRSSIInfratructure = null;
		if (debug) {
			int frameWidth = 900, frameHeight = 240;
			frameRSSIInfratructure = new FunctionFrame("test (actual)",
					filterManagerInfrastructure.getActualDataDB(), 95, 40);
			frameRSSIInfratructure.setBounds(10, 10, frameWidth, frameHeight);
			frameRSSIInfratructure.setVisible(true);
			frameRSSIInfratructure.toFront();

			filteredRSSIInfratructure = new FunctionFrame("test (filtered)",
					filterManagerInfrastructure.getFilteredDataDB(), 95, 40);
			filteredRSSIInfratructure.setBounds(10, 10 + frameHeight,
					frameWidth, frameHeight);
			filteredRSSIInfratructure.setVisible(true);
			filteredRSSIInfratructure.toFront();
		}

		// iteration=0;
		while (ok) {
			try {
				Vector<NamedLocationOss> rssiListInfrastructure;// = new
																// Vector<LocationOss>();
				Vector<NamedLocationOss> rssiListAdhoc = new Vector<NamedLocationOss>();

				// 1) RSSI gathering
				rssiListInfrastructure = ieee80211.getRSSIList();
				for (int i = 0; i < rssiListInfrastructure.size(); i++) {
					if (((BSS) (rssiListInfrastructure.elementAt(i))).getType() != BSS.INFRASTRUCTURE) {
						rssiListAdhoc.addElement(rssiListInfrastructure
								.elementAt(i));
						rssiListInfrastructure.removeElementAt(i);
						i--;
					}
				}
				// System.out.println("MobilityEstimator rssiList "+rssiList);

				// 2) db population & filtering
				filterManagerInfrastructure
						.feedPredictor(rssiListInfrastructure);
				filterManagerAdhoc.feedPredictor(rssiListAdhoc);
				// insertData(rssi,harmonic);
				// System.out.println("MobilityEstimator actualDataDB "+filterManager.getActualDataDB());
				// System.out.println("MobilityEstimator filteredDataDB "+filterManager.getFilteredDataDB());

				// 3) First Harmonic max module computation
				Set<String> macs = filterManagerInfrastructure
						.getFilteredDataDB().keySet();
				Iterator<String> macsIt = macs.iterator();
				float maxModule = -1;
				// System.out.println("MobilityEstimator macs "+macs);
				while (macsIt.hasNext()) {
					String aMac = macsIt.next();
					Vector<Double> aSequence = filterManagerInfrastructure
							.getFilteredDataDB().get(aMac);
					try {
						// System.out.println("MobilityEstimator aSequence "+aSequence+"    harmonic "+harmonic);
						float module = getModule(aSequence, harmonic);
						// System.out.println("MobilityEstimator module "+module);
						if (module > maxModule)
							maxModule = module;
					} catch (Exception e) {
						// do nothing
						// e.printStackTrace(System.out);
					}
				}
				if (debug)
					System.out.println("MobilityEstimatorIEEE80211 maxModule "
							+ maxModule);

				// 4) Mobility Degree computation
				if (maxModule == -1)
					mobilityDegree = -1;
				else if (maxModule <= lowerBound)
					mobilityDegree = 0; // Prima soglia di stima: probabilita'
										// di movimento e' 0
				else if (maxModule >= upperBound)
					mobilityDegree = 1; // Seconda soglia di stima: probabilita'
										// di movimento e' 1
				/* (maxModule>lowerBound && maxModule<upperBound) */// maxModule-1.3;
																	// //
																	// Probabilita'
																	// di
																	// movimento
																	// e' pari a
																	// mobility
				else
					mobilityDegree = (1 / (upperBound - lowerBound))
							* maxModule
							+ (1 - ((upperBound + lowerBound) / (upperBound - lowerBound)))
							/ 2;
				// System.out.println("MobilityEstimator mobilityDegree "+mobilityDegree);

				// 5) Mobility State computation
				if (mobilityDegree == -1)
					mobilityState = MobilityEstimator.NOT_AVAILABLE;
				else if (mobilityDegree >= 0.6 /*
												 * &&
												 * mobilityState!=MobilityEstimator
												 * .MOTION
												 */) {
					mobilityState = MobilityEstimator.MOTION;
				} else if (mobilityDegree <= 0.4 /*
												 * &&
												 * mobilityState!=MobilityEstimator
												 * .STILL
												 */) {
					mobilityState = MobilityEstimator.STILL;
				}
				// System.out.println("MobilityEstimator mobilityState "+mobilityState);

				// 6) Joint computation
				Vector<LocationOss> jointVectorTemp = new Vector<LocationOss>();
				Set<String> macsAdhoc = filterManagerAdhoc.getFilteredDataDB()
						.keySet();
				Iterator<String> macsItAdhoc = macsAdhoc.iterator();
				// System.out.println("MobilityEstimator macs "+macs);
				while (macsItAdhoc.hasNext()) {
					String aMacAdhoc = macsItAdhoc.next();
					Vector<Double> aSequenceAdhoc = filterManagerAdhoc
							.getFilteredDataDB().get(aMacAdhoc);
					try {
						// System.out.println("MobilityEstimator aSequence "+aSequence+"    harmonic "+harmonic);
						float moduleAdhoc = getModule(aSequenceAdhoc, harmonic);
						// System.out.println("MobilityEstimator module "+module);

						float joint;
						if (moduleAdhoc == -1)
							joint = -1;
						else if (moduleAdhoc <= lowerBound)
							joint = 1;
						else if (moduleAdhoc >= upperBound)
							joint = 0;
						/* (module>lowerBound && module<upperBound) */
						else
							joint = (1 / (lowerBound - upperBound))
									* moduleAdhoc
									- (upperBound / (lowerBound - upperBound));
						jointVectorTemp.addElement(new LocationOss(aMacAdhoc,
								"" + joint));
					} catch (Exception e) {
						// do nothing
						// e.printStackTrace(System.out);
					}
					jointVector = jointVectorTemp;
				}
				// Iteration finished
				// RSSI draw
				if (debug) {
					frameRSSIAdhoc.setTitle("Test (actual) - current AP: ???");// +
																				// currentAPaddress);
					frameRSSIAdhoc.draw();
					filteredRSSIAdhoc
							.setTitle("Test (filtered) - current AP: ???");// +
																			// currentAPaddress);
					filteredRSSIAdhoc.draw();
				}
				if (debug) {
					frameRSSIInfratructure
							.setTitle("Test (actual) - current AP: ???");// +
																			// currentAPaddress);
					frameRSSIInfratructure.draw();
					filteredRSSIInfratructure
							.setTitle("Test (filtered) - current AP: ???");// +
																			// currentAPaddress);
					filteredRSSIInfratructure.draw();
				}

				sleep(samplePeriod);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		// networkInterface80211.stopNetworkInterface80211();
	}

	/*
	 * public boolean isNoise(int iterations){
	 * System.out.println("isNoise iterations: "+iterations); FilterManager
	 * noiseFilterManager=new FilterManager();
	 * noiseFilterManager.setMinimizeValueSet(true);
	 * noiseFilterManager.setFilterMemory(iterations);
	 * 
	 * Vector<LocationOss> rssiList; // 1) read values for(int
	 * i=0;i<iterations;i++){ System.out.println("iteration i: "+i);
	 * rssiList=networkInterface80211.getRSSIList();
	 * noiseFilterManager.feedPredictor(rssiList); try{
	 * Thread.sleep(samplePeriod); }catch(Exception e){ e.printStackTrace(); } }
	 * 
	 * // 2) gather standard deviation Iterator<String>
	 * iterator=noiseFilterManager.getActualDataDB().keySet().iterator(); double
	 * maxStdDev=-1.0; while(iterator.hasNext()){ String ap=iterator.next(); //
	 * the db has at least 3/4 of required values
	 * System.out.println("size: "+noiseFilterManager
	 * .getActualDataDB().get(ap).size
	 * ()+"  (iterations*0.75: "+iterations*0.75+")");
	 * 
	 * 
	 * //if(noiseFilterManager.getActualDataDB().get(ap).size()>=(iterations*0.75
	 * )){ double
	 * stdDev=standardDeviation(noiseFilterManager.getActualDataDB().get(ap));
	 * System.out.println("stdDev: "+stdDev); if(stdDev>maxStdDev){
	 * maxStdDev=stdDev; } //} } System.out.println("maxStdDev: "+maxStdDev);
	 * 
	 * // 3) if maxStdDev>threshold => return true if(maxStdDev>1.0){ // TODO
	 * quale valore??? return true; } else{ return false; } }
	 * 
	 * public double standardDeviation(Vector<Double> rssiValues){ // compute
	 * standard deviation int count=0; double somm=0; for(int
	 * i=0;i<rssiValues.size();i++){ if(rssiValues.elementAt(i)!=null){
	 * somm+=rssiValues.elementAt(i); count++; } } double average=somm/count;
	 * System.out.println("average: "+average+"  (value:"+count+")");
	 * 
	 * count=0; somm=0; for(int i=0;i<rssiValues.size();i++){
	 * if(rssiValues.elementAt(i)!=null){
	 * somm+=Math.pow(rssiValues.elementAt(i)-average,2); count++; } } double
	 * standardDeviation=Math.sqrt(somm/count);
	 * System.out.println("standardDeviation: "
	 * +standardDeviation+"  (value:"+count+")");
	 * if(count>=(rssiValues.size()*0.75)){ return standardDeviation; } else{
	 * return -1; } }
	 */

	private float getModule(Vector<Double> valueSet, int harmonic)
			throws Exception {
		int setSize = valueSet.size();
		// System.out.println("MobilityEstimator setSize "+setSize);

		// int pmax=harmonic*4;
		// int exploitedValues=4;
		int exploitedValues = setSize;
		// if(valueSet.size()<pmax) throw new
		// Exception("Unable to compute Fourier Coefficients");

		for (int p = 0; p < setSize / 2 + 1; p++) {// at most N/2 fourier
													// coefficients
			double tempA = 0;
			double tempB = 0;
			// int max=/*N*/pmax;
			for (int n = 0; n < exploitedValues/* pmax */; n++) {
				tempA = tempA
						+ 2.0
						/ exploitedValues/* pmax */
						* valueSet
								.elementAt((setSize - exploitedValues/* pmax */)
										+ n)
						* Math
								.cos(2.0 * Math.PI * p * n / exploitedValues/* pmax */);
				tempB = tempB
						+ 2.0
						/ exploitedValues/* pmax */
						* valueSet
								.elementAt((setSize - exploitedValues/* pmax */)
										+ n)
						* Math
								.sin(2.0 * Math.PI * p * n / exploitedValues/* pmax */);
			}

			double real = tempA / 2;
			double imm = -tempB / 2;
			double mod = Math.sqrt(real * real + imm * imm);
			// double ph=Math.atan(imm/real);

			// System.out.println(p+" mod: "+mod);
			/*
			 * if(p==0){ // RSSI too low // => // RSSI fluctuations primarily
			 * due to signal noise // instead of mobile node movements
			 * if(mod>=75)return -1; }
			 */
			if (p == harmonic) {
				return (float) mod;
			}
		}

		System.err.println("Unable to compute Fourier Coefficients");
		throw new Exception("Unable to compute Fourier Coefficients");
	}
}
